本文目的旨在对http状态码含义做一个基本解释,不会太深入讨论每个状态码

http状态码分类

  1. 消息类(1字头)
  2. 成功类(2字头)
  3. 重定向类(3字头)
  4. 请求错误类(4字头)
  5. 服务器错误类(5、6字头)

中途可能会穿插nginx自定义的http状态码,nginx状态码本身不属于http状态码了,只是在nginx内部自己定义的一套状态码,但是在nginx日志中,却经常出现

请求错误类(客户端错误)

400

400的英文含义400 Request Header Or Cookie Too Large,顾名思义,头信息或者Cookie信息太多了
复现这个状态码只需要添加够长的头信息或者Cookie信息即可,构造一个curl请求:

1
curl --header "Cookie:sidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidisidisisidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisidisidissidisisidisidisisidisidisisidisidisisidisidisisidisidisisi....." http://localhost:80

执行这个curl命令可以看到提示:
img

401

401的含义是401 Authorization Required,顾名思义,就是需要权限认证,但是客户端又没有通过认证。复现这个状态码必须对nginx调整成认证模式。现在,将nginx调成认证模式,nginx的server模块配置如下

1
location / {    auth_basic "secret";#认证模式    auth_basic_user_file /usr/local/nginx/passwd.db;#密码文件    root   html2;    index  index.html index.htm;}

接下来生成密码文件

1
sh-3.2# htpasswd -c /usr/local/nginx/passwd.db yongxiongzhongNew password: Re-type new password: Adding password for user yongxiongzhong

更改权限

1
chmod 400 /usr/local/nginx/passwd.db

平滑重启nginx之后,再次访问网页可以看到认证界面
img
点击取消按钮,则可以看到以下这个页面
img

403

403的出现,大部分是没有对文件进行授权。403 Forbidden顾名思义就是禁止访问,重现这个状态码只需要修改访问文件的权限,比如给nginx网站根目录中的index文件减少权限

1
chmod 0 /usr/local/nginx/html/index.html

当我们再次访问这个文件是,就会出现403错误
img

404

404算是我们经常碰到的状态码,404 Not Found当我们访问一个不存在的文件时,就会出现这个错误
在URL地址栏上随便访问一个不存在的文件,就会出现
img
在实际生产环境中,这样的404页面并不好看,所以可以通过在server中配置自定义404页面:

1
error_page  404              /my_404.html;

其中在网站根目录新建my_404.html然后写入自定义内容。平滑重启nginx之后,再次访问一些不存在的页面时会提示如下:
img

405

405状态码并不算常见,它表示405 Not Allowed。http请求可以支持GET,POST,PUT,DELETE方式。默认情况下,如果你对一个html静态文件进行post请求的话,就会出现405错误
img

413

413也是比较容易出现的一种状态码,413的出现常常伴随着413 Request Entity Too Large表示请求实体过大导致。用户上传的Content-Length大于nginx设定的最大值时。比如上传一张很大的图片,就会出现413错误码
img
这个是由参数client_max_body_size控制的,默认是1M。有点小,所以一般线上环境调成以下配置即可消除上面的问题

1
client_max_body_size 8m;

414

一般出现这个错误的时候,也伴随着一段英文提示414 Request-URI Too Large,也就是说我们请求的url太长了,如果我们把一个很长的url放在浏览器地址栏上,浏览器的保护措施,并不会出现414报错。所以要重现这个414错误码,只能通过curl命令。申请如下一个很长的url,篇幅问题,最后用…代替:

1
curl http://localhost:8080/?key=abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg....

运行这个curl命令,就可以看到414错误
图片标题
在nginx中,以下两个参数共同决定这个问题

1
client_header_buffer_sizelarge_client_header_buffers

499(nginx status)

499这个状态码并不是http协议中定义的status code,而是nginx自己定义的一个状态码。
当客户端主动断开连接的时候,nginx就会返回499的状态码。按照这个状态码的定义,复现这个状态码很容易,只要在nginx返回结果之前断开客户端连接。所以,下一个data.php文件

1
<?phpsleep(10);//睡觉10秒钟

然后在浏览器访问http://localhost:8000/data.php,注意,在10秒之内关闭浏览器以断开客户端连接。然后在查看nginx访问日志就能看到499错误
img
值得一提的是,在线上环境中,如果并发量大的话,nginx未能及时处理完请求,导致客户端“不干了”,这是会大量爆发499错误。这种情况可以用ab工具测试,ab工具中,-n为请求次数,-c为并发量:

1
ab -n 100 -c 100 http://127.0.0.1:8000/data.php

再观察我们的nginx访问日志,会发现大量的499
img

服务端错误

500

http状态码500表示内部服务器错误,这个错误一般是php代码出现error导致,如果你没有关闭php错误提示,当写一个错误的php脚本时,网页上会出现以下错误提示
img
但是一般情况下这些错误我们并不希望就这样暴露给客户端,因为将这些错误信息暴露是一件很危险的事情,别人可以通过你的错误猜测系统漏洞。所以在线上一般是关闭错误显示,关闭方式为:编辑php-fpm.conf关闭错误信息,保存php-fpm以下设置项

1
php_flag[display_errors] = off

平滑重启php-fpm进程之后,再次访问一个包含错误语法的php错误的时候,会报500错误。
img

502

当出现502这个错误的时候,也伴随着一句英文502 bad geteway,很醒目的一段问题,出现这个错误的时候,因为这是服务端错误,可以定位挂掉了。Nginx 502错误的原因比较多,是因为在代理模式下后端服务器出现问题引起的。这些错误一般都不是nginx本身的问题,一定要从后端找原因。比如这里复现一种后端php-fpm进程挂掉的情况,关闭php-fpm

1
kill -9 `ps aux | grep php-fpm | grep -v grep | awk -F ' ' '{print $2}'`

再次访问我们的php文件时候,然后就可以看到网站挂掉的情况
img

503

注意,出现503的时候服务没挂,出现503的时候伴随着503 Service Temporarily Unavailable,这句话告诉我们服务是暂时性不可用,nginx官方文档上有说明这一点,

Sets the shared memory zone and the maximum allowed number of connections for a given key value. When this limit is exceeded, the server will return the 503 (Service Temporarily Unavailable) error in reply to a request.

简单的说,就是在控制请求频率和并发数,详细配置可以参考nginx官方文档:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html#limit_conn
这里只复现一种可能的情况,将nginx配置如下(两个核心配置,省去了很多):

1
http {    limit_conn_zone $binary_remote_addr zone=addr:10m;    server {        limit_conn addr 1;#并发数为1,好测试

注意测试的时候,不是所有的连接都算进去,也就是并发数为1,并不代表只能并发1,nginx说明:

Not all connections are counted. A connection is counted only if it has a request processed by the server and the whole request header has already been read.

然后采用ab测试工具,运行如下命令

1
ab -n 2 -c 2 http://127.0.0.1:8000/data.php

查看nginx访问日志,可以看到503报错信息
img

504

当出现504的时候也伴随着一段英文,504 Gateway Time-out,顾名思义,就是超时了,复现这个错误码也很简单。让你的php程序模拟耗时请求,比如把php脚本里添加以下内容

1
<?phpsleep(70);//模拟耗时,睡70秒echo "睡醒了";

然后通过域名访问这个脚本文件,就会出现超时的界面
img

重定向和缓存

301和302

之所以将这两个状态码放到一起,因为他们都是重定向,其中,301永久重定向302暂时重定向。不管是暂时还是临时,对用户而言,这两者没什么区别,都是在访问A网站的时候跳转到了B网站,并看到浏览器上的地址栏变成了B网站的地址。有区别的是搜索引擎,搜索引擎是要建立索引规则和权重的,如果网站A被设定为永久重定向到B,那搜索引擎可以确定A的地址永久改变了,就会把B当做唯一有效的目标地址,这是搜索引擎会把老地址的PageRank等信息带到新地址,同时在搜索引擎索引库中彻底废弃掉原先的老地址。所以,所以只要网站不是临时性迁移,都会做301重定向。
在nginx的rewrite语法中有两个关键字
permanent——永久重定向 redirect——临时重定向
如果我们想要将.asp文件永久重定向到index页面

1
location ~ \.asp$ {    rewrite ^(.*)$ /index.html permanent;}

img
如果只想临时重定向到index页面。

1
location ~ \.asp$ {    rewrite ^(.*)$ /index.html redirect;}

再次访问页面是可以看到302冲重定向了,同样,浏览器地址栏上的地址变成了重定向后的地址
img

304

304 Not Modified,默认情况下,nginx会对静态文件进行缓存。为了节省网络宽带,nginx和浏览器会产生如下交互

1
1. 浏览器客户端想请求一个文档,如果浏览器本地已经有缓存了,发送If-Modified-Since给Web服务器2. 服务器将文件最后修改时间将服务器的文档修改时间Last-Modified和浏览器发送过来的If-Modified-Since比较,如果两者一致,返回304给浏览器,告诉浏览器用本地缓存就好了,如果两者不一致,返回200给浏览器,告诉浏览器使用最新的文档

在默认情况下,在浏览器中输入http://localhost:8000/test.css请求css文件两次就会出现304
img

如果在服务器端编辑test.css文件,由于这个时间文件最后修改时间发生了变更,这时服务器不会返回304状态码了。这时返回200状态码,两个的时间不一样了,浏览器采用最新的。

img

有关于各种缓存策略这里不做讨论。